package com.seed.core.pojo.seed;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.seed.core.annotation.Habtm;
import com.seed.core.pojo.Const;
import com.seed.util.RowKit;
import com.seed.util.SeedKit;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Habtms extends HasManys {
    protected String throughSeedName;
    protected Seed throughSeed;
    protected Class<?> throughEntityClass;
    protected String throughSelfColumn;
    protected String throughTargetColumn;

//    public Habtms of(Class<?> selfEntityClass, Class<?> targetEntityClass, Habtm habtm) {
//        setExtraType(habtm.extraType());
//        setSelfEntityClass(selfEntityClass);
//        setTargetEntityClass(targetEntityClass);
//        setSelfColumn(habtm.selfColumn());
//        setTargetColumn(habtm.targetColumn());
////        setCondition(habtm.condition());
//        setLabel(habtm.label());
//        setComment(habtm.comment());
//        setCascade(habtm.cascade());
//        setLimit(habtm.limit());
//        setExtra(habtm.extra());
//
//        setThroughEntityClass(habtm.through());
//        setThroughSelfColumn(habtm.throughSelfColumn());
//        setThroughTargetColumn(habtm.throughTargetColumn());
//
//        return this;
//    }

    public HasOnes of2(Map<Class<?>, Seed> seeds, Class<?> selfEntityClass, Class<?> targetEntityClass, Habtm habtm) {
        setSelfSeed(seeds.get(selfEntityClass));
        setTargetSeed(seeds.get(targetEntityClass));
        setTargetSeedName(targetSeed.getName());

        setSelfEntityClass(selfEntityClass);
        setTargetEntityClass(targetEntityClass);

        setSelfColumn(habtm.selfColumn());
        setTargetColumn(habtm.targetColumn());
        setLabel(habtm.label());
        setComment(habtm.comment());

        setExtra(habtm.extra());

        setCascade(habtm.cascade());
        setLimit(habtm.limit());

        setThroughEntityClass(habtm.through());
        setThroughSelfColumn(habtm.throughSelfColumn());
        setThroughTargetColumn(habtm.throughTargetColumn());
        setThroughSeed(seeds.get(throughEntityClass));
        setThroughSeedName(throughSeed.getName());

        return this;
    }

    @Override
    public Seed getExtraSeed() {
        return throughSeed;
    }

    public Habtms clone() {
        Habtms ret = new Habtms();
        ret.setTargetSeedName(targetSeedName);
        ret.setSelfSeed(selfSeed);
        ret.setTargetSeed(targetSeed);
        ret.setSelfEntityClass(selfEntityClass);
        ret.setTargetEntityClass(targetEntityClass);
        ret.setSelfColumn(selfColumn);
        ret.setTargetColumn(targetColumn);
        ret.setLabel(label);
        ret.setComment(comment);

        if(extra != null) ret.setExtra(new HashMap<>(extra));

        ret.setCascade(cascade);
        ret.setLimit(limit);

        ret.setThroughSeedName(throughSeedName);
        ret.setThroughSeed(throughSeed);
        ret.setThroughEntityClass(throughEntityClass);
        ret.setThroughSelfColumn(throughSelfColumn);
        ret.setThroughTargetColumn(throughTargetColumn);

        return ret;
    }

//    public Map<TableType, Set<String>> getThroughConditionColumns() {
//        if(throughConditionColumns == null) {
//            throughConditionColumns = new HashMap<>();
//            if(throughCondition != null) {
//                for(String c: throughCondition) initThroughConditionItem(throughConditionColumns, c);
//            }
//        }
//
//        return throughConditionColumns;
//    }

//    protected void initThroughConditionItem(Map<TableType, Set<String>> container, String conditionItem) {
//        List<String> list = StrKit.bracket(conditionItem, '`', '`');
//        for(String item: list) {
//            if(!item.contains(".")) throw new RuntimeException("列必需写成【self.列】或【through.列】的形式");
//
//            String[] ownerAndColumn = StrKit.cutInTwo(item, '.', true);
//            String owner = ownerAndColumn[0];
//            String column = ownerAndColumn[1];
//            if(StrUtil.isBlank(owner)) {
//                throw new RuntimeException("列必需以【self, through】任何一个开头");
//            }
//            TableType tableType = TableType.valueOf(owner);
//
//            switch (tableType) {
//                case self -> checkColumn(selfSeed, column);
//                case target -> throw new RuntimeException("【throughCondition】不能引用【target】");
//                case through -> checkColumn(throughSeed, column);
//            }
//
//            Set<String> columnNames = container.get(tableType);
//            if(columnNames == null) columnNames = new HashSet<>();
//            columnNames.add(column);
//            container.put(tableType, columnNames);
//        }
//    }

//    public QueryCondition getQueryCondition(String basePath, String cross) {
//        throw new RuntimeException("不支持此方法调用");
//    }

    public QueryCondition getQueryCondition(List<?> selfList, String targetTableAlias) {
        throw new RuntimeException("不支持此方法调用");
    }

    public QueryCondition getQueryCondition(Object self, String targetTableAlias) {
        throw new RuntimeException("不支持此方法调用");
    }

//    public QueryCondition getQueryCondition(Object self, String throughAlias, String targetTableAlias) {
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        String inferThroughTargetColumn = inferThroughTargetColumn();
//        String inferTargetColumn = inferTargetColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        if(condition == null || condition.length == 0) {
//            return new QueryColumn(targetTable, inferTargetColumn).eq(new QueryColumn(throughTable, inferThroughTargetColumn))
//                    .and(new QueryColumn(throughTable, inferThroughSelfColumn).eq(RowKit.getSelfValue(self, selfField())));
//        }
//
//        QueryCondition ret = hasThroughCrossField()
//                ? new QueryColumn(throughTable, inferThroughSelfColumn).eq(RowKit.getSelfValue(self, selfField()))
//                : QueryCondition.createEmpty();
//
//        if(hasCrossField()) {
//            ret.and(new QueryColumn(targetTable, inferTargetColumn).eq(new QueryColumn(throughTable, inferThroughTargetColumn)));
//        }
//
//        for(String c: condition) {
//            Map<TableType, Set<String>> columns = getConditionColumns();
//            for(Map.Entry<TableType, Set<String>> entry: columns.entrySet()) {
//                TableType tableType = entry.getKey();
//                Set<String> columnNames = entry.getValue();
//                for (String columnName: columnNames) {
//                    switch (tableType) {
//                        case self -> {
//                            Object selfValue = RowKit.getSelfValue(self, columnName);
//                            String realValue = selfValue instanceof String ? String.format("'%s'", selfValue) : selfValue.toString();
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), realValue);
//                        }
//                        case through -> {
//                            String replacement = String.format("`%s.%s`", throughAlias, columnName);
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                        }
//                        case target -> {
//                            String replacement = String.format("`%s.%s`", targetTableAlias, columnName);
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                        }
//                    }
//                }
//            }
//            ret.and(c);
//        }
//
//        return ret;
//    }

//    public QueryCondition getQueryCondition(Object self, String throughAlias, String targetTableAlias) {
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        String inferThroughTargetColumn = inferThroughTargetColumn();
//        String inferTargetColumn = inferTargetColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        Object selfValue = RowKit.getSelfValue(self, StrUtil.toCamelCase(inferSelfColumn()));
//        QueryCondition ret = new QueryColumn(targetTable, inferTargetColumn)
//                .eq(new QueryColumn(throughTable, inferThroughTargetColumn))
//                .and(new QueryColumn(throughTable, inferThroughSelfColumn).eq(selfValue));
//
//        for(Map.Entry<String, Object> entry: getColumnAndValues().entrySet()) {
//            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
//        }
//
//        return ret;
//    }

//    public QueryCondition getQueryCondition(List<?> selfList, String throughAlias, String targetTableAlias) {
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        String inferThroughTargetColumn = inferThroughTargetColumn();
//        String inferSelfField = StrUtil.toCamelCase(inferSelfColumn());
//        String inferTargetColumn = inferTargetColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        List<Object> selfValues = selfList.stream().map(self -> RowKit.getSelfValue(self, inferSelfField)).toList();
//        QueryCondition ret = new QueryColumn(targetTable, inferTargetColumn)
//                .eq(new QueryColumn(throughTable, inferThroughTargetColumn))
//                .and(new QueryColumn(throughTable, inferThroughSelfColumn).in(selfValues));
//
//        for(Map.Entry<String, Object> entry: getColumnAndValues().entrySet()) {
//            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
//        }
//
//        return ret;
//    }

//    public QueryCondition getQueryCondition(String selfTableAlias, String throughAlias, String targetTableAlias) {
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        String inferThroughTargetColumn = inferThroughTargetColumn();
//        String inferTargetColumn = inferTargetColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        if(condition == null || condition.length == 0) {
//            return new QueryColumn(throughTable, inferThroughTargetColumn).eq(new QueryColumn(targetTable, inferTargetColumn));
//        }
//
//        QueryCondition ret = hasCrossField()
//                ? new QueryColumn(throughTable, inferThroughTargetColumn).eq(new QueryColumn(targetTable, inferTargetColumn))
//                : QueryCondition.createEmpty();
//
//        for(String c: condition) {
//            Map<TableType, Set<String>> columns = getConditionColumns();
//            for(Map.Entry<TableType, Set<String>> entry: columns.entrySet()) {
//                TableType tableType = entry.getKey();
//                String alias = switch (tableType) {
//                    case self -> selfTableAlias;
//                    case target -> targetTableAlias;
//                    case through -> throughAlias;
//                };
//                Set<String> columnNames = entry.getValue();
//                for (String columnName: columnNames) {
//                    String replacement = String.format("`%s.%s`", alias, columnName);
//                    c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                }
//            }
//            ret.and(c);
//        }
//
//        return ret;
//    }

    public QueryCondition getQueryCondition(String throughTableAlias, String targetTableAlias) {
        QueryTable throughTable = new QueryTable(throughSeed.getTableName()).as(throughTableAlias);
        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
        String inferThroughTargetColumn = inferThroughTargetColumn();
        String inferTargetColumn = inferTargetColumn();

        QueryCondition ret = new QueryColumn(targetTable, inferTargetColumn).eq(new QueryColumn(throughTable, inferThroughTargetColumn));
        for(Map.Entry<String, Object> entry: SeedKit.formatExtra(getExtraSeed(), getExtra()).entrySet()) {
            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
        }

        return ret;
    }

//    public QueryCondition getThroughQueryCondition(String selfTableAlias, String throughAlias) {
//        QueryTable selfTable = new QueryTable(selfSeed.getTableName()).as(selfTableAlias);
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        String[] throughCondition = getThroughCondition();
//        if(throughCondition == null || throughCondition.length == 0) {
//            return new QueryColumn(selfTable, inferSelfColumn()).eq(new QueryColumn(throughTable, inferThroughSelfColumn));
//        }
//
//        QueryCondition ret = hasThroughCrossField()
//                ? new QueryColumn(selfTable, inferSelfColumn()).eq(new QueryColumn(throughTable, inferThroughSelfColumn))
//                : QueryCondition.createEmpty();
//
//        Map<TableType, Set<String>> columns = getThroughConditionColumns();
//        for(String c: throughCondition) {
//            for(Map.Entry<TableType, Set<String>> entry: columns.entrySet()) {
//                TableType tableType = entry.getKey();
//                Set<String> columnNames = entry.getValue();
//                for (String columnName: columnNames) {
//                    switch (tableType) {
//                        case self -> {
//                            String replacement = String.format("`%s.%s`", selfTableAlias, columnName);
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                        }
//                        case through -> {
//                            String replacement = String.format("`%s`.`%s`", throughAlias, columnName);
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                        }
//                    }
//                }
//            }
//            ret.and(c);
//        }
//
//        return ret;
//    }

//    public QueryCondition getThroughQueryCondition(String selfTableAlias, String throughAlias) {
//        QueryTable selfTable = new QueryTable(selfSeed.getTableName()).as(selfTableAlias);
//        QueryTable throughTable = new QueryTable(throughSeed.getTableName()).as(throughAlias);
//        String inferSelfColumn = inferSelfColumn();
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//
//        QueryCondition ret = new QueryColumn(throughTable, inferThroughSelfColumn).eq(new QueryColumn(selfTable, inferSelfColumn));
//        for(Map.Entry<String, Object> entry: getColumnAndValues().entrySet()) {
//            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
//        }
//
//        return ret;
//    }

//    public QueryCondition getThroughQueryCondition1(String targetTableAlias, String throughAlias) {
//        QueryTable targetTable = new QueryTable(targetSeed.getTableName()).as(targetTableAlias);
//        QueryTable throughTable = new QueryTable(throughSeed.getTableName()).as(throughAlias);
//        String inferTargetColumn = inferTargetColumn();
//        String inferThroughTargetColumn = inferThroughTargetColumn();
//
//        QueryCondition ret = new QueryColumn(throughTable, inferThroughTargetColumn).eq(new QueryColumn(targetTable, inferTargetColumn));
//        for(Map.Entry<String, Object> entry: getColumnAndValues().entrySet()) {
//            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
//        }
//
//        return ret;
//    }

//    public QueryCondition getThroughQueryCondition(Object self, String throughAlias) {
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        String[] throughCondition = getThroughCondition();
//        if(throughCondition == null || throughCondition.length == 0) {
//            String selfField = StrUtil.toCamelCase(inferSelfColumn());
//            Object selfValue = RowKit.getSelfValue(self, selfField);
//            return new QueryColumn(throughTable, inferThroughSelfColumn).eq(selfValue);
//        }
//
//        QueryCondition ret = hasThroughCrossField()
//                ? new QueryColumn(throughTable, inferThroughSelfColumn).eq(RowKit.getSelfValue(self, selfField()))
//                : QueryCondition.createEmpty();
//
//        Map<TableType, Set<String>> columns = getThroughConditionColumns();
//        for(String c: throughCondition) {
//            for(Map.Entry<TableType, Set<String>> entry: columns.entrySet()) {
//                TableType tableType = entry.getKey();
//                Set<String> columnNames = entry.getValue();
//                for (String columnName: columnNames) {
//                    switch (tableType) {
//                        case self -> {
//                            Object selfValue = RowKit.getSelfValue(self, columnName);
//                            String realValue = selfValue instanceof String ? String.format("'%s'", selfValue) : selfValue.toString();
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), realValue);
//                        }
//                        case through -> {
//                            if(StrUtil.isBlank(throughAlias)) {
//                                String replacement = String.format("`%s`", columnName);
//                                c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                            } else {
//                                String replacement = String.format("`%s.%s`", throughAlias, columnName);
//                                c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                            }
//                        }
//                    }
//                }
//            }
//            ret.and(c);
//        }
//
//        return ret;
//    }

//    public QueryCondition getThroughQueryCondition(Object self, String throughAlias) {
//        QueryTable throughTable = new QueryTable(throughSeed.getTableName()).as(throughAlias);
//        String inferTargetColumn = inferTargetColumn();
//
//        String selfField = StrUtil.toCamelCase(inferSelfColumn());
//        QueryCondition ret = new QueryColumn(throughTable, inferTargetColumn).eq(RowKit.getSelfValue(self, selfField));
//        for(Map.Entry<String, Object> entry: getColumnAndValues().entrySet()) {
//            ret.and(new QueryColumn(throughTable, entry.getKey()).eq(entry.getValue()));
//        }
//
//        return ret;
//    }

//    public QueryCondition getThroughQueryConditionBySelfValue(Object selfValue, String throughAlias) {
//        String inferThroughSelfColumn = inferThroughSelfColumn();
//        QueryTable throughTable = new QueryTable(throughTableName()).as(throughAlias);
//
//        String[] throughCondition = getThroughCondition();
//        if(throughCondition == null || throughCondition.length == 0) {
//            return new QueryColumn(throughTable, inferThroughSelfColumn).eq(selfValue);
//        }
//
//        QueryCondition ret = hasThroughCrossField()
//                ? new QueryColumn(throughTable, inferThroughSelfColumn).eq(selfValue)
//                : QueryCondition.createEmpty();
//
//        Map<TableType, Set<String>> columns = getThroughConditionColumns();
//        for(String c: throughCondition) {
//            for(Map.Entry<TableType, Set<String>> entry: columns.entrySet()) {
//                TableType tableType = entry.getKey();
//                Set<String> columnNames = entry.getValue();
//                for (String columnName: columnNames) {
//                    switch (tableType) {
//                        case self -> {
//                            Object selfValue = RowKit.getSelfValue(self)
//                            String realValue = selfValue instanceof String ? String.format("'%s'", selfValue) : selfValue.toString();
//                            c = c.replace(String.format("`%s.%s`", tableType, columnName), realValue);
//                        }
//                        case through -> {
//                            if(StrUtil.isBlank(throughAlias)) {
//                                String replacement = String.format("`%s`", columnName);
//                                c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                            } else {
//                                String replacement = String.format("`%s.%s`", throughAlias, columnName);
//                                c = c.replace(String.format("`%s.%s`", tableType, columnName), replacement);
//                            }
//                        }
//                    }
//                }
//            }
//            ret.and(c);
//        }
//
//        return ret;
//    }

    public String inferTargetColumn() {
        if(StrUtil.isBlank(targetColumn)) return Const.ID;

        return StrUtil.toUnderlineCase(targetColumn);
    }

    public String inferThroughSelfColumn() {
        if(StrUtil.isBlank(throughSelfColumn)) {
            return StrUtil.toUnderlineCase(StrUtil.lowerFirst(selfSeed.getName())) + "_" + Const.ID;
        }

        return StrUtil.toUnderlineCase(throughSelfColumn);
    }

    public String inferThroughTargetColumn() {
        if(StrUtil.isBlank(throughTargetColumn)) {
            return StrUtil.toUnderlineCase(StrUtil.lowerFirst(targetSeed.getName())) + "_" + Const.ID;
        }

        return StrUtil.toUnderlineCase(throughTargetColumn);
    }

    /**
     * 是否有中间表关联列
     * @return
     */
//    public boolean hasThroughCrossField() {
//        return throughCondition == null || StrUtil.isNotBlank(selfColumn) || StrUtil.isNotBlank(throughSelfColumn);
//    }
//
//    public boolean hasCrossField() {
//        return condition == null || StrUtil.isNotBlank(throughTargetColumn) || StrUtil.isNotBlank(targetColumn);
//    }

    public String throughTableName() {
        return throughSeed.getTableName();
    }

    public String getThroughSeedName() {
        return throughSeedName;
    }

    public void setThroughSeedName(String throughSeedName) {
        this.throughSeedName = throughSeedName;
    }

    public String throughSelfColumn() {
        return StrUtil.toUnderlineCase(throughSelfColumn);
    }

    public String throughTargetColumn() {
        return StrUtil.toUnderlineCase(throughTargetColumn);
    }

    public String throughSelfField() {
        return StrUtil.toCamelCase(throughSelfColumn);
    }

    public String throughTargetField() {
        return StrUtil.toCamelCase(throughTargetColumn);
    }

    public Seed getThroughSeed() {
        return throughSeed;
    }

    public void setThroughSeed(Seed throughSeed) {
        this.throughSeed = throughSeed;
    }

    public Class<?> getThroughEntityClass() {
        return throughEntityClass;
    }

    public void setThroughEntityClass(Class<?> throughEntityClass) {
        this.throughEntityClass = throughEntityClass;
    }

    public String getThroughSelfColumn() {
        return throughSelfColumn;
    }

    public void setThroughSelfColumn(String throughSelfColumn) {
        this.throughSelfColumn = throughSelfColumn;
    }

    public String getThroughTargetColumn() {
        return throughTargetColumn;
    }

    public void setThroughTargetColumn(String throughTargetColumn) {
        this.throughTargetColumn = throughTargetColumn;
    }

//    public String[] getThroughCondition() {
//        return throughCondition;
//    }
//
//    public void setThroughCondition(String[] throughCondition) {
//        if(throughCondition == null) {
//            this.throughCondition = null;
//        } else {
//            throughCondition = Stream.of(throughCondition).filter(StrUtil::isNotBlank).toArray(String[]::new);
//            this.throughCondition = throughCondition.length > 0 ? throughCondition : null;
//        }
//    }

//    private void assertNotCodeType() {
//        if(extraType == ExtraType.code) {
//            throw new RuntimeException(String.format("多对多关系不支持【%s】类型", ExtraType.code.getLabel()));
//        }
//    }
}
